#include "Shader.h"

#include<GLFW/glfw3.h>
#include<iostream>
#include<fstream>
#include<sstream>
#include "../../glsl/stb_image.h"

using namespace std;

Shader::Shader() {
}

void Shader::SetUniform3f(unsigned int shaderId, const char *nameString, glm::vec3 param) {
    glUniform3f(glGetUniformLocation(shaderId, nameString), param.x, param.y, param.z);
}

void Shader::SetUniform1f(unsigned int shaderId, const char *nameString, float param) {
    glUniform1f(glGetUniformLocation(shaderId, nameString), param);
}

void Shader::SetUniform1i(unsigned int shaderId, const char *nameString, unsigned int slot) {
    glUniform1i(glGetUniformLocation(shaderId, nameString), slot);
}


unsigned int Shader::loadShader(string vertexPath, string fragPath) {
    unsigned int ID;
    ifstream vertexFile;
    ifstream fragFile;
    stringstream vertexStream;
    stringstream fragStream;
    vertexFile.open(vertexPath);
    fragFile.open(fragPath);
    vertexFile.exceptions(static_cast<_Ios_Iostate>(ifstream::failbit || ifstream::badbit));
    fragFile.exceptions(static_cast<_Ios_Iostate>(ifstream::failbit || ifstream::badbit));

    try {
        if (!vertexFile.is_open() || !fragFile.is_open()) {
//            throw exception()
            printf("material open fail");
        }
        vertexStream << vertexFile.rdbuf();
        fragStream << fragFile.rdbuf();
        vertexString = vertexStream.str();
        fragString = fragStream.str();
        vertexSource = vertexString.c_str();
        fragSource = fragString.c_str();
        unsigned int vertex, frag;


        vertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex, 1, &vertexSource, NULL);
        glCompileShader(vertex);

        chechError(vertex, "vertex");

        frag = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(frag, 1, &fragSource, NULL);
        glCompileShader(frag);

        chechError(frag, "fragment");

        ID = glCreateProgram();
        glAttachShader(ID, vertex);
        glAttachShader(ID, frag);
        glLinkProgram(ID);
        chechError(ID, "program");

        glDeleteShader(vertex);
        glDeleteShader(frag);
    }
    catch (const std::exception &ex) {
        printf(ex.what());
    }
    this->ID = ID;
    return ID;
}
void Shader::loadTexture(GLenum glenum, GLint color, const char *imagePath) {
    unsigned int texture;
    glGenTextures(1, &texture);
    glActiveTexture(glenum);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_set_flip_vertically_on_load(true);
    int width, height, channel;
    unsigned char *data = stbi_load(imagePath, &width, &height, &channel, 0);

    if (data) {
        glTexImage2D(GL_TEXTURE_2D, 0, color, width, height, 0, color, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    } else {
        cout << "failed to load texture" << endl;
    }
    stbi_image_free(data);
}
void Shader::chechError(unsigned int ID, string type) {

    int success;
    char infoLog[512];
    if (type != "program") {
        glGetShaderiv(ID, GL_COMPILE_STATUS, &success);
        if (!success) {
            glGetShaderInfoLog(ID, 512, NULL, infoLog);
            cout << "material compile error" << infoLog << endl;
        }
    } else {
        glGetProgramiv(ID, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(ID, 512, NULL, infoLog);
            cout << "program compile error" << infoLog << endl;
        }
    }
}